<?php
abstract class BaseEntity extends Zend_Db_Table
{
    const INITIALIZED_VERSION = 0;

    protected $_primary = 'id';

    private $_id;
    private $_ver;
    private $_createTime;
    private $_lastModifyTime;

    private $_base = array('id', 'ver', 'createTime', 'lastModifyTime');

    abstract protected function map();

    private function lowerFirstChar($property)
    {
        return strtolower(substr($property, 0, 1)).substr($property, 1);
    }

    public function __call($method, $args)
    {
        $property = ltrim($method, 'get');
        $property = $this->lowerFirstChar($property);

        $tmp = '_'.$property;
        return isset($this->{$tmp})?$this->{$tmp}:'';
    }

    public function toArray()
    {/*{{{*/
        return $this->convertMap2Data();
    }/*}}}*/

    //return obj
    public function load($ids, $needRowset=false)
    {/*{{{*/
        if (is_array($ids))
        {
            return $this->find($ids);
        }
        else
        {
            $db = $this->getAdapter();
            $where = $db->quoteInto('id= ?', $ids);
            $res = $this->fetchRow($where);
            if (false == $needRowset)
            {
                return $this->row2obj($res);
            }
        }
    }/*}}}*/

    //return array
    public function findByIds($ids, $needArray=true)
    {/*{{{*/
        $res = parent::find($ids);
        if ($needArray)
        {
            return $res->toArray();
        }
        return $res;
    }/*}}}*/

    public function save()
    {/*{{{*/
        $data = $this->convertMap2Data();

        $data['ver'] += 1;
        if ($this->isCreated())
        {
            $this->_id = $this->insert($data);
            $this->_ver++;
        }
        else
        {
            $db = &$this->getAdapter();
            $data['lastModifyTime'] = date('Y-m-d H:i:s');
            $where = $db->quoteInto('id = ?', $this->_id);
            //$affected = $this->update($data, $where);
            $this->update($data, $where);
        }
    }/*}}}*/
    
    protected function createBase($id, $ver, $createTime, $lastModifyTime)
    {/*{{{*/
        $this->_id = $id;
        $this->_ver = $ver;
        $this->_createTime = $createTime;
        $this->_lastModifyTime = $lastModifyTime;
    }/*}}}*/
    
    protected function create($arr)
    {/*{{{*/
        foreach ($this->getPropMap() as $key)
        {
            $tmp = '_'.$key;
            $this->$tmp = isset($arr[$key])?$arr[$key]:'';
        }
    }/*}}}*/
    
    protected function row2obj($rowset)
    {/*{{{*/
        if (null == $rowset)
        {
            $className = get_class($this);
            $nullObjClsName = $className.'NullObject';
            if (class_exists($nullObjClsName))
            {
                return new $nullObjClsName();
            }
            throw new BizException(gettext('not_exist'));    
        }
        $arr = $rowset->toArray();
        return $this->create($arr);
    }/*}}}*/

    protected function getSessSvc()
    {/*{{{*/
        $registry = Zend_Registry::getInstance();
        return $registry->get('sessvc');
    }/*}}}*/

    protected function createId()
    {/*{{{*/
        return IdGenter::getInstance()->createId();
    }/*}}}*/
    
    protected function createUserId()
    {/*{{{*/
        return IdGenter::getInstance()->createId('user');
    }/*}}}*/

    private function convertMap2Data()
    {/*{{{*/
        $map = $this->getPropMap();
        for ($i=0,$max=count($map);$i<$max;$i++)
        {
            $key = $map[$i];
            $property = '_'.$key;
            $val = $this->{$property};
            $data[$key] = $val;
        }
        return $data;
    }/*}}}*/
    
    private function getPropMap()
    {/*{{{*/
        return array_merge($this->_base, $this->map());
    }/*}}}*/

    private function isCreated()
    {/*{{{*/
        return $this->_ver == self::INITIALIZED_VERSION;
    }/*}}}*/
}

class NullObject
{
    public function __call($method, $args)
    {
        return '';
    }

    public function save()
    {
        throw new SysException('no obj!');    
    }
}
